import { ElMessage, UploadRawFile } from 'element-plus'
import { AxiosResponse } from 'axios'
import { ProTableBoolType, ProTableData } from './interface'
import { DownloadFileType } from '@/api/system/file/type'
import { OneOf } from '@/types/type'
import { ProTableApi } from '@/api/common/proTableApi'
import { QueryCondition } from '@/types/queryform'

/**
 * ProTable配置
 */
interface ProTableConfigs {
    /**
     * 根据传入的控制器名称，自动生成 Table 所需的通用请求方法
     */
    api: ProTableApi
    /**
     * 已经获取的，可以直接传入 useTable 使用的数据
     */
    data?: any[]
    /**
     * 排序字段，默认为 createDateTime（创建时间）
     */
    sort?: string
    /**
     * 搜索字段，默认为：'name'
     */
    search?: string
    /**
     * 排序顺序，顺序 Or 倒序，默认为 true (顺序)
     */
    order?: boolean
    /**
     * 默认查询条件
     */
    queryConditions?: QueryCondition[]
    /**
     * 是否分页
     */
    isPage?: boolean
    /**
     * 数据改变后，是window重新载入还是刷新Table，默认为false，刷新Table
     */
    isReload?: boolean
    /**
     * 请求是否立即执行，默认为true，立即执行
     */
    immediate?: boolean
}

/**
 *
 * @param configs ProTable配置
 * @returns
 */
export function useTable(configs: ProTableConfigs) {
    const table = reactive<ProTableData>({
        data: [],
        page: {
            total: 0,
            currentPage: 1,
            pageSize: 20,
            pageCount: 0,
        },
        queryForm: {
            sort: configs.sort ?? 'createDateTime',
            order: configs.order ?? true,
            queryConditions: configs.queryConditions ?? [],
        },
        defaultQueryConditions: [],
        dynamicQuery: false,
        isRefresh: false,
        defaultExpandAll: false,

        seletion: [],
        currentRow: null,
    })

    const formRef = ref()
    const proTableRef = ref()
    const assignFormRef = ref()
    const importFormRef = ref()

    onMounted(() => {
        if (configs.immediate !== false) {
            setBoolValue('isRefresh', true)
        }
    })

    watch(
        () => table.isRefresh,
        () => {
            onRefreshData()
        },
    )

    /**
     * 刷新table数据
     * @returns
     */
    const onRefreshData = () => {
        if (table.isRefresh && proTableRef.value) {
            table.data = []
            getData()
            nextTick(() => {
                setTimeout(() => {
                    table.isRefresh = false
                }, 200)
            })
        }
    }
    /**
     * 操作后置
     * - 数据改变后，是 window 重新载入还是刷新 Table ，默认为 false，刷新 Table
     */
    const onActionAfter = () => {
        configs.isReload === true ? window.location.reload() : setBoolValue('isRefresh', true)
    }
    /**
     * 设置 ProTable 的布尔值
     * @param type 要修改的布尔属性
     * @param value 修改后的布尔值
     */
    const setBoolValue = (type: keyof ProTableBoolType, value: boolean) => {
        table[type] = value
    }

    /**
     * 获得数据
     */
    const getData = async () => {
        if (table.defaultQueryConditions.length !== 0) {
            table.defaultQueryConditions.forEach((item1) => {
                if (table.queryForm.queryConditions.every((item2) => item2.name !== item1.name)) {
                    table.queryForm.queryConditions.push(item1)
                }
            })
        }
        if (configs?.data) {
            table.data = configs?.data
        } else if (configs.isPage !== false) {
            const result = await configs?.api.getPage({
                currentPage: table.page.currentPage,
                pageSize: table.page.pageSize,
                ...table.queryForm,
            })

            if (result.isSuccess) {
                const { items, currentPage, pageSize, total, pageCount } = result.data
                table.data = items
                table.page.currentPage = currentPage
                // table.page.pageSize = pageSize
                table.page.total = total
                table.page.pageCount = pageCount
            }
        } else {
            const result = await configs?.api.getAll(table.queryForm)
            if (result.isSuccess) {
                table.data = result.data
            }
        }
    }
    /**
     * 根据数据id获得数据
     * @param id 数据Id
     * @returns
     */
    const getById = async (id: number) => {
        const result = await configs.api.getById(id)
        if (result.isSuccess) {
            return result.data
        }
    }
    /**
     * 根据数据id数组获得数据
     * @param ids 数据id数组
     * @returns
     */
    const getByIds = async (ids: number[]) => {
        const result = await configs.api.getByIds(ids)
        if (result.isSuccess) {
            return result.data
        }
    }
    /** 添加或者编辑数据的方法
     * @param data 要添加或者编辑的数据
     * @returns
     */
    const addOrEditData = async (data: any) => {
        const result = await configs.api.addOrEdit(data)
        if (result.isSuccess) {
            ElMessage.success(data.id ? '更新成功' : '添加成功')
            formRef.value.onCloseDialog()
            onActionAfter()
        } else {
            ElMessage.error(data.id ? '更新失败' : '添加失败')
        }
    }
    /**
     * 分配权限
     * @param id 数据Id
     * @param ids 权限Id数组
     */
    const assignPermissions = async (id: number, ids: number[]) => {
        const result = await configs.api.assignPermissions(id, ids)
        if (result.isSuccess) {
            ElMessage.success('分配权限成功')
            assignFormRef.value.onCloseDialog()
            onActionAfter()
        } else {
            ElMessage.error('分配权限失败')
        }
    }
    /** 切换数据布尔值的方法
     * @param id 要切换的数据Id
     * @param message 要切换的属性标题，用于成功后发送消息使用，例如 '禁用' Or '启用'
     */
    const switchBoole = async (id: number, message = '切换') => {
        ElMessageBox.confirm(`确定要${message}这条数据吗？`, {
            title: '提示',
            type: 'warning',
        })
            .then(async () => {
                const result = await configs.api.enabled(id)
                if (result.isSuccess) {
                    ElMessage.success(`${message}成功`)
                    onActionAfter()
                } else {
                    ElMessage.error(`${message}失败`)
                }
            })
            .catch((error) => {
                if (error === 'cancel') {
                    ElMessage.info(`已取消${message}`)
                }
            })
    }
    /** 删除数据的方法
     * @param id 要删除的数据Id
     */
    const deleteData = async (id: number) => {
        if (id === 0 && table.seletion.length === 1) {
            id = table.seletion[0].id
        } else if (id === 0 && table.seletion.length === 0) {
            ElMessage.warning('请先选择要删除的数据！')
            return
        }
        ElMessageBox.confirm('确定要删除这条数据吗？', {
            title: '提示',
            type: 'warning',
        })
            .then(async () => {
                const result = await configs.api.delete(id)
                if (result.isSuccess) {
                    ElMessage.success('删除成功')
                    table.page.currentPage = table.data?.length > 1 ? table.page.currentPage : table.page.currentPage - 1
                    onActionAfter()
                } else {
                    ElMessage.error('删除失败')
                }
            })
            .catch((error) => {
                if (error === 'cancel') {
                    ElMessage.info('已取消删除')
                }
            })
    }
    /**
     *  批量删除数据的方法
     */
    const batchDeleteData = async () => {
        const ids: number[] = []
        table.seletion.forEach((item) => {
            ids.push(item.id)
        })
        if (ids.length === 0) {
            ElMessage.warning('请先选择要删除的数据！')
            return
        }
        ElMessageBox.confirm('确定要批量删除这些数据吗？', {
            title: '提示',
            type: 'warning',
        })
            .then(async () => {
                const result = await configs.api.batchDelete(ids)
                if (result.isSuccess) {
                    ElMessage.success('批量删除成功')
                    table.page.currentPage = table.data?.length > ids.length ? table.page.currentPage : table.page.currentPage - 1
                    onActionAfter()
                } else {
                    ElMessage.error('批量删除失败')
                }
            })
            .catch((error) => {
                if (error === 'cancel') {
                    ElMessage.info('已取消批量删除')
                }
            })
    }
    /**
     * 批量导入数据
     * @param data 要导入的数据
     */
    const importData = async (data: any) => {
        const result = await configs.api.import(data)
        if (result.isSuccess) {
            ElMessage.success('导入成功')
            importFormRef.value.onCloseDialog()
            onActionAfter()
        } else {
            ElMessage.error('导入失败')
        }
    }
    /**
     * 同步api接口
     */
    const syncApiData = async () => {
        ElMessageBox.confirm('确定要同步接口数据吗？', {
            title: '提示',
            type: 'warning',
        })
            .then(async () => {
                const result = await syncApi()
                if (result.isSuccess) {
                    ElMessage.success('同步成功')
                    onActionAfter()
                } else {
                    ElMessage.error('同步失败')
                }
            })
            .catch((error) => {
                if (error === 'cancel') {
                    ElMessage.info('已取消同步')
                }
            })
    }
    /**
     * 导出数据到excel文件
     * @param isExportData 是导出数据还是导出模版，默认导出数据
     * @returns
     */
    const exportData = async (isExportData = true) => {
        try {
            let response: AxiosResponse<any, any>
            let filename = '导出文件.xlsx'
            if (isExportData) {
                const ids = table.seletion.map((item): number => item.id)
                if (ids.length === 0) {
                    ElMessage.warning('请先选择要导出的数据')
                    return
                }
                response = await configs.api.export(ids)
            } else {
                response = await configs.api.exportTemplate()
                filename = '模版文件.xlsx'
            }

            if (response.status !== 200) {
                ElMessage.error('导出失败')
                return
            }

            filename = getFileName(response, filename)

            // 创建一个带有文件名的URL
            const url = window.URL.createObjectURL(new Blob([response.data]))
            // 创建一个隐藏的 <a> 元素用于下载
            const link = document.createElement('a')
            link.href = url
            link.download = filename
            // 模拟点击
            link.click()
            // 释放对象URL
            window.URL.revokeObjectURL(url)
            ElMessage.success('导出成功')
        } catch (error) {
            console.log(error)
            ElMessage.error('导出失败')
        }
    }
    /**
     * 上传
     * @param file 要上传的文件
     */
    const upload = async (file: UploadRawFile) => {
        const result = await uploadFile(file)
        if (result.isSuccess) {
            ElMessage.success('上传成功')
            onActionAfter()
        } else {
            ElMessage.error('上传失败')
        }
    }
    /**
     * 下载
     * @param file 下载文件参数
     * @returns
     */
    const download = async (file: OneOf<DownloadFileType>, isHeaderAction = false) => {
        try {
            if (isHeaderAction) {
                const ids = table.seletion.map((item): number => item.id)
                if (ids.length === 0) {
                    ElMessage.warning('请先选择要下载的文件')
                    return
                }
                file = { ids }
            }
            const response = await downloadFile(file)
            if (response.status !== 200) {
                ElMessage.error('下载失败')
                return
            }
            // 获取文件名，优先尝试从响应头部Content-Disposition中解析出来
            let filename = getFileName(response, '下载文件.zip')

            // 创建一个带有文件名的URL
            const url = window.URL.createObjectURL(new Blob([response.data]))
            // 创建一个隐藏的 <a> 元素用于下载
            const link = document.createElement('a')
            link.href = url
            link.download = filename
            // 模拟点击
            link.click()
            // 释放对象URL
            window.URL.revokeObjectURL(url)
            ElMessage.success('下载成功')
        } catch (error) {
            console.log(error)
            ElMessage.error('下载失败')
        }
    }
    /**
     * 折叠或者展开所有
     * @param boole 折叠还是展开
     * @returns
     */
    const expandAll = () => {
        if (!proTableRef.value) return
        proTableRef.value.onExpandOrFoldAll(!table.defaultExpandAll)
        setBoolValue('defaultExpandAll', !table.defaultExpandAll)
    }
    /**
     * 根据当前控制器名称，打开不同的分配权限表单
     * @param value 要获得权限的数据
     */
    const onOpenAssignDiagle = (value: any) => {
        switch (configs.api.controllerUrl) {
            case 'user':
                assignFormRef.value.onOpenDialog(value.id, value.roleIds)
                break
            case 'role':
                assignFormRef.value.onOpenDialog(value.id, value.menuIds)
                break
            case 'menu':
                assignFormRef.value.onOpenDialog(value.id, value.apiIds)
                break
            default:
                break
        }
    }
    /**
     * 排序
     * @param data 包含当前位置数据id和目标位置数据id
     */
    const onSortable = async (data: any) => {
        const result = await configs.api.sortable(data)
        if (result.isSuccess) {
            ElMessage.success('排序成功')
            onActionAfter()
        } else {
            ElMessage.error('排序失败')
        }
    }
    /** 重置用户密码
     * @param id 要重置密码的数据Id
     */
    const onPasswordReset = async (id: number) => {
        let ids: number[] = []
        if (id === 0 && table.seletion.length === 0) {
            ElMessage.warning('请先选择要重置密码的数据！')
            return
        } else if (id === 0 && table.seletion.length !== 0) {
            table.seletion.forEach((item) => ids.push(item.id))
        }
        const result = await resetPassword(id, ids)
        if (result.isSuccess) {
            ElMessage.success('重置成功')
        } else {
            ElMessage.error('重置失败')
        }
    }

    const onTableAction = async (action: TableActionType, params: any) => {
        if (!action) return
        switch (action) {
            case 'refresh':
                setBoolValue('isRefresh', true)
                break
            case 'selection-change':
                table.seletion = params
                break
            case 'row-click':
                table.currentRow = params
                break
            case 'current-change':
                table.page.currentPage = params
                setBoolValue('isRefresh', true)
                break
            case 'size-change':
                table.page.pageSize = params
                table.page.currentPage = 1
                setBoolValue('isRefresh', true)
                break
            case 'add':
                formRef.value.onOpenDialog('add')
                break
            case 'edit':
                formRef.value.onOpenDialog('edit', params)
                break
            case 'enabled':
                await switchBoole(params.id, params.message)
                break
            case 'addOrEditSubmit':
                await addOrEditData(params)
                break
            case 'assign':
                onOpenAssignDiagle(params)
                break
            case 'assignPermissionsSubmit':
                await assignPermissions(params.id, params.ids)
                break
            case 'delete':
                await deleteData(params.id)
                break
            case 'batchDelete':
                await batchDeleteData()
                break
            case 'import':
                importFormRef.value.onOpenDialog()
                break
            case 'import-form-submit':
                await importData(params)
                break
            case 'export':
                await exportData()
                break
            case 'export-excelTemplate':
                await exportData(false)
                break
            case 'sync':
                await syncApiData()
                break
            case 'expandAll':
                expandAll()
                break
            case 'search':
                table.queryForm = {
                    sort: configs.sort ?? 'createDateTime',
                    order: configs.order ?? true,
                    queryConditions:
                        params === '' ? (configs.queryConditions ?? []) : [{ name: configs.search ?? 'name', value: params, operator: 'Contains' }],
                }
                setBoolValue('isRefresh', true)
                break
            case 'query-form-submit':
                table.queryForm = params
                setBoolValue('isRefresh', true)
                break
            case 'dynamicQuery':
                setBoolValue('dynamicQuery', !table.dynamicQuery)
                break
            case 'dragSort':
                await onSortable(params)
                break
            case 'view':
                ElMessage.error('此功能还未完善')
                break
            case 'upload':
                await upload(params)
                break
            case 'download': {
                if (params.id === 0) {
                    await download(params, true)
                } else {
                    await download({ id: params.id })
                }
                break
            }
            case 'reset':
                await onPasswordReset(params.id)
                break

            default:
                break
        }
    }

    return {
        table,
        formRef,
        assignFormRef,
        proTableRef,
        importFormRef,
        setBoolValue,
        getById,
        getByIds,
        addOrEditData,
        switchBoole,
        importData,
        exportData,
        deleteData,
        batchDeleteData,
        expandAll,
        onTableAction,
    }
}
